package userinfo

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"gitee.com/Flearning/go_crawl/modules/mgobson"
	"gitee.com/Flearning/go_crawl/utils"
)

var (
	userAgent           string = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36"
	reqCookie           string = "u=621499062484038; device_id=e352204f2567caaf79bb2ea6d7f604e1; s=f614827afj; webp=0; aliyungf_tc=AQAAAI6oogfmiggAftPBdnxRV/8mxO4/; xq_a_token=0a52c567442f1fdd8b09c27e0abb26438e274a7e; xq_r_token=43c6fed2d6b5cc8bc38cc9694c6c1cf121d38471; Hm_lvt_1db88642e346389874251b5a1eded6e3=1499062485,1499237925; Hm_lpvt_1db88642e346389874251b5a1eded6e3=1499240295; __utma=1.28700194.1499063782.1499063782.1499238384.2; __utmb=1.15.10.1499238384; __utmc=1; __utmz=1.1499063782.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)"
	friendshipsUri      string = "https://xueqiu.com/friendships/groups/members.json?page=%s&uid=%s&gid=0"
	mgoXueqiuCollection string = "crawl_xueqiu_user"
	MgoWSet             bool
)

type (
	CrawlInfo struct {
		UriPath string
		Page    string
		UserId  string
	}

	BodyJsonFormat struct {
		Count   int              `json:"count"`
		Page    int              `json:"page"`
		MaxPage int              `json:"maxPage"`
		Users   *json.RawMessage `json:"users"`
	}

	UserStruct struct {
		Id_                 bson.ObjectId `bson:"_id,omitempty" json:"mgoid,omitempty"`
		StatusCount         int           `bson:"status_count,omitempty" json:"status_count,omitempty"`
		Following           bool          `bson:"following,omitempty" json:"following,omitempty"`
		BlogDescription     string        `bson:"blog_description,omitempty" json:"blog_description,omitempty"`
		Verified            bool          `bson:"verified,omitempty" json:"verified,omitempty"`
		FriendsCount        int           `bson:"friends_count,omitempty" json:"friends_count,omitempty"`
		Intro               string        `bson:"intro,omitempty" json:"intro,omitempty"`
		Profile             string        `bson:"profile,omitempty" json:"profile,omitempty"`
		Step                string        `bson:"step,omitempty" json:"step,omitempty"`
		DonateCount         int           `bson:"donate_count,omitempty" json:"donate_count,omitempty"`
		Gender              string        `bson:"gender,omitempty" json:"gender,omitempty"`
		StColor             string        `bson:"st_color,omitempty" json:"st_color,omitempty"`
		GroupIds            string        `bson:"group_ids,omitempty" json:"group_ids,omitempty"`
		StocksCount         int           `bson:"stocks_count,omitempty" json:"stocks_count,omitempty"`
		Type                string        `bson:"type,omitempty" json:"type,omitempty"`
		Province            string        `bson:"province,omitempty" json:"province,omitempty"`
		NamePinyin          string        `bson:"name_pinyin,omitempty" json:"name_pinyin,omitempty"`
		CubeCount           int           `bson:"cube_count,omitempty" json:"cube_count,omitempty"`
		RecommendReason     string        `bson:"recommend_reason,omitempty" json:"recommend_reason,omitempty"`
		Blocking            bool          `bson:"blocking,omitempty" json:"blocking,omitempty"`
		Remark              string        `bson:"remark,omitempty" json:"remark,omitempty"`
		CommonCount         int           `bson:"common_count,omitempty" json:"common_count,omitempty"`
		Location            string        `bson:"location,omitempty" json:"location,omitempty"`
		Id                  int           `bson:"id,omitempty" json:"id,omitempty"`
		StockStatusCount    string        `bson:"stock_status_count,omitempty" json:"stock_status_count,omitempty"`
		Recommend           string        `bson:"recommend,omitempty" json:"recommend,omitempty"`
		City                string        `bson:"city,omitempty" json:"city,omitempty"`
		Subscribeable       bool          `bson:"subscribeable,omitempty" json:"subscribeable,omitempty"`
		VerifiedDescription string        `bson:"verified_description,omitempty" json:"verified_description,omitempty"`
		VerifiedType        int           `bson:"verified_type,omitempty" json:"verified_type,omitempty"`
		ProfileImageUrl     string        `bson:"profile_image_url,omitempty" json:"profile_image_url,omitempty"`
		PhotoDomain         string        `bson:"photo_domain,omitempty" json:"photo_domain,omitempty"`
		Url                 string        `bson:"url,omitempty" json:"url,omitempty"`
		AllowAllStock       bool          `bson:"allow_all_stock,omitempty" json:"allow_all_stock,omitempty"`
		Status              int           `bson:"status,omitempty" json:"status,omitempty"`
		Name                string        `bson:"name,omitempty" json:"name,omitempty"`
		Description         string        `bson:"description,omitempty" json:"description,omitempty"`
		ScreennamePinyin    string        `bson:"screenname_pinyin,omitempty" json:"screenname_pinyin,omitempty"`
		Domain              string        `bson:"domain,omitempty" json:"domain,omitempty"`
		FollowMe            bool          `bson:"follow_me,omitempty" json:"follow_me,omitempty"`
		ScreenName          string        `bson:"screen_name,omitempty" json:"screen_name,omitempty"`
		LastStatusId        int           `bson:"last_status_id,omitempty" json:"last_status_id,omitempty"`
		VerifiedRealname    bool          `bson:"verified_realname,omitempty" json:"verified_realname,omitempty"`
		FollowersCount      int           `bson:"followers_count,omitempty" json:"followers_count,omitempty"`
		AddTime             time.Time     `bson:"add_time" json:"-"`
	}
)

func NewCrawlInfo(ci *CrawlInfo) *CrawlInfo {
	return &CrawlInfo{
		UriPath: friendshipsUri,
		Page:    ci.Page,
		UserId:  ci.UserId,
	}
}

func (c *CrawlInfo) CrawlUser(page, userid string) (int, []byte) {
	if page == "0" || page == "" {
		page = c.Page
	}
	if userid == "0" || userid == "" {
		userid = c.UserId
	}
	uriPath := fmt.Sprintf(c.UriPath, page, userid)
	return httpNewRequest(uriPath)
}

func httpNewRequest(urlStr string) (statusCode int, respBody []byte) {
	var err error
	tr := &http.Transport{
		MaxIdleConns:       10,
		IdleConnTimeout:    30 * time.Second,
		DisableCompression: true,
	}

	client := &http.Client{
		Transport: tr,
	}

	req, _ := http.NewRequest("GET", urlStr, nil)
	req.Header.Set("User-Agent", userAgent)
	req.Header.Add("Cookie", reqCookie)
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("httpNewRequest error: ", err)
	}
	statusCode = resp.StatusCode
	if statusCode == http.StatusOK {
		respBody, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("httpNewRequest ReadAll error: ", err)
		}
		return
	}
	return statusCode, nil
}

func (r *BodyJsonFormat) BsonData(rb []byte) []UserStruct {
	rbf := new(BodyJsonFormat)
	json.Unmarshal(rb, rbf)
	bsonD := make([]UserStruct, 0)
	json.Unmarshal(*rbf.Users, &bsonD)
	return bsonD
}

func addNowForBsonD(u []UserStruct) []interface{} {
	var bsonD []interface{}
	for _, vU := range u {
		vU.AddTime = time.Now()
		bsonD = append(bsonD, vU)
	}
	return bsonD
}

func crawlUserIds(u []UserStruct) []string {
	var uids []string
	for _, vU := range u {
		uidStr := fmt.Sprintf("%d", vU.Id)
		uids = append(uids, uidStr)
	}
	return uids
}

//type CrewlResp struct {
//	RespCount int
//	MaxPage int
//	MgoCount int
//	ClowNum int

//}

func UserCrawl(page, userid string) (statusCode int, maxPage int, nextPage int, uids []string) {
	var (
		bsonD        []UserStruct
		respBodyBtye []byte
	)
	uc := &CrawlInfo{
		Page:   page,
		UserId: userid,
	}
	uid := uc.UserId
	fmt.Println("page: ", uc.Page)
	newUc := NewCrawlInfo(uc)
	statusCode, respBodyBtye = newUc.CrawlUser(uc.Page, uid)
	nextPage, _ = utils.StrTo(uc.Page).Int()
	if statusCode != http.StatusOK {
		nextPage, _ = utils.StrTo(uc.Page).Int()
		return
	}
	j2s := new(BodyJsonFormat)
	json.Unmarshal(respBodyBtye, j2s)
	// fmt.Println("MaxPage:", j2s.MaxPage, "Page:", j2s.Page)
	bsonD = j2s.BsonData(respBodyBtye)

	if MgoWSet {
		bsonI := addNowForBsonD(bsonD)
		RunInstert(bsonI...)
		fmt.Println("this crawl data length: ", len(bsonD))
	}

	maxPage = j2s.MaxPage
	if len(bsonD) > 0 {
		uids = crawlUserIds(bsonD)
	}
	if maxPage > nextPage {
		nextPage++
	}

	return
}

func RunInstert(v ...interface{}) error {
	query := func(c *mgo.Collection) error {
		return c.Insert(v...)
	}
	return mgobson.WatchCollection(mgoXueqiuCollection, query)
}

func RunCount() (int, error) {
	var count int
	var err error
	query := func(c *mgo.Collection) error {
		count, err = c.Count()
		return err
	}
	err = mgobson.WatchCollection(mgoXueqiuCollection, query)
	return count, err
}
